// Copyright DustedPixels.com 2008. All rights reserved.

package com.dustedpixels.jasmin.unit.z80.v1;

import com.dustedpixels.common.StringUtils;

/**
 * Z80 registers: (B, C, D, E, H, L, W, Z) + alternate set and (PC, SP, IX, IY)
 * 
 * @author micapolos@gmail.com (Michal Pociecha-Los)
 */
public final class Regs {
  public static final int MODE_8BIT = 0;
  public static final int MODE_16BIT = 1;
  
  public static final int REG_B = 0x00;
  public static final int REG_C = 0x01;
  public static final int REG_D = 0x02;
  public static final int REG_E = 0x03;
  public static final int REG_H = 0x04;
  public static final int REG_L = 0x05;
  public static final int REG_W = 0x06;
  public static final int REG_Z = 0x07;
  public static final int REG_PCh = 0x08;
  public static final int REG_PCl = 0x09;
  public static final int REG_SPh = 0x0A;
  public static final int REG_SPl = 0x0B;
  public static final int REG_IXh = 0x0C;
  public static final int REG_IXl = 0x0D;
  public static final int REG_IYh = 0x0E;
  public static final int REG_IYl = 0x0F;
  
  public static final int REG_BC = 0x00;
  public static final int REG_DE = 0x02;
  public static final int REG_HL = 0x04;
  public static final int REG_WZ = 0x06;
  public static final int REG_PC = 0x08;
  public static final int REG_SP = 0x0A;
  public static final int REG_IX = 0x0C;
  public static final int REG_IY = 0x0E;
  
  public int DATA_IN;
  public int REG_IN;
  public int MODE_IN;
  
  public int DATA_OUT;
  public int REG_OUT;
  public int MODE_OUT;
  
  public boolean RESET;
  public boolean WR;
  public boolean EXX;
  
  final int[][] bcdehlwz = new int[2][8];
  int pc;
  int sp;
  final int[] index = new int[2];
  
  int regSet; 

  public void updateSeq() {
    if (RESET) {
      bcdehlwz[0][0] = 0;
      bcdehlwz[0][1] = 0;
      bcdehlwz[0][2] = 0;
      bcdehlwz[0][3] = 0;
      bcdehlwz[0][4] = 0;
      bcdehlwz[0][5] = 0;
      bcdehlwz[0][6] = 0;
      bcdehlwz[0][7] = 0;

      bcdehlwz[1][0] = 0;
      bcdehlwz[1][1] = 0;
      bcdehlwz[1][2] = 0;
      bcdehlwz[1][3] = 0;
      bcdehlwz[1][4] = 0;
      bcdehlwz[1][5] = 0;
      bcdehlwz[1][6] = 0;
      bcdehlwz[1][7] = 0;
      
      pc = 0;
      sp = 0;
      index[0] = 0;
      index[1] = 0;
    } else if (WR) {
      switch (MODE_IN) {
        case MODE_8BIT:
          switch (REG_IN) {
            case REG_B:
            case REG_C:
            case REG_D:
            case REG_E:
            case REG_H:
            case REG_L:
            case REG_W:
            case REG_Z:
              bcdehlwz[regSet][REG_IN & 0x07] = DATA_IN;
              break;
            case REG_PCh:
              pc = (pc & 0x00FF) | (DATA_IN & 0xFF) << 8;
              break;
            case REG_PCl:
              pc = (pc & 0xFF00) | (DATA_IN & 0xFF);
              break;
            case REG_SPh:
              sp = (sp & 0x00FF) | (DATA_IN & 0xFF) << 8;
              break;
            case REG_SPl:
              sp = (sp & 0xFF00) | (DATA_IN & 0xFF);
              break;
            case REG_IXh:
              index[0] = (index[0] & 0x00FF) | (DATA_IN & 0xFF) << 8;
              break;
            case REG_IXl:
              index[0] = (index[0] & 0xFF00) | (DATA_IN & 0xFF);
              break;
            case REG_IYh:
              index[1] = (index[1] & 0x00FF) | (DATA_IN & 0xFF) << 8;
              break;
            case REG_IYl:
              index[1] = (index[1] & 0xFF00) | (DATA_IN & 0xFF);
              break;
          }
          break;
        case MODE_16BIT:
          switch (REG_IN) {
            case REG_B:
            case REG_C:
            case REG_D:
            case REG_E:
            case REG_H:
            case REG_L:
            case REG_W:
            case REG_Z:
              bcdehlwz[regSet][REG_IN & 0x06] = DATA_IN >>> 8;
              bcdehlwz[regSet][REG_IN & 0x06 | 0x01] = DATA_IN & 0xFF;
              break;
            case REG_PCh:
            case REG_PCl:
              pc = DATA_IN;
              break;
            case REG_SPh:
            case REG_SPl:
              sp = DATA_IN;
              break;
            case REG_IXh:
            case REG_IXl:
              index[0] = DATA_IN;
              break;
            case REG_IYh:
            case REG_IYl:
              index[1] = DATA_IN;
              break;
          }
          break;
      }
    } else if (EXX) {
      regSet ^= 1;
    }
  }

  public void updateComb() {
    switch (MODE_OUT) {
      case MODE_8BIT:
        switch (REG_OUT) {
          case REG_B:
          case REG_C:
          case REG_D:
          case REG_E:
          case REG_H:
          case REG_L:
          case REG_W:
          case REG_Z:
            DATA_OUT = bcdehlwz[regSet][REG_OUT & 0x07];
            break;
          case REG_PCh:
            DATA_OUT = pc >> 8;
            break;
          case REG_PCl:
            DATA_OUT = pc & 0xFF;
            break;
          case REG_SPh:
            DATA_OUT = sp >> 8;
            break;
          case REG_SPl:
            DATA_OUT = sp & 0xFF;
            break;
          case REG_IXh:
            DATA_OUT = index[0] >> 8;
            break;
          case REG_IXl:
            DATA_OUT = index[0] & 0xFF;
            break;
          case REG_IYh:
            DATA_OUT = index[1] >> 8;
            break;
          case REG_IYl:
            DATA_OUT = index[1] & 0xFF;
            break;
        }
        break;
      case MODE_16BIT:
        switch (REG_OUT) {
          case REG_B:
          case REG_C:
          case REG_D:
          case REG_E:
          case REG_H:
          case REG_L:
          case REG_W:
          case REG_Z:
            DATA_OUT = 
                (bcdehlwz[regSet][REG_OUT & 0x06] << 8) |
                (bcdehlwz[regSet][REG_OUT & 0x06 | 0x01]);
            break;
          case REG_PCh:
          case REG_PCl:
            DATA_OUT = pc;
            break;
          case REG_SPh:
          case REG_SPl:
            DATA_OUT = sp;
            break;
          case REG_IXh:
          case REG_IXl:
            DATA_OUT = index[0];
            break;
          case REG_IYh:
          case REG_IYl:
            DATA_OUT = index[1];
            break;
        }
        break;
    }
  }
  
  void printState() {
    System.out.println(
        "B = " + StringUtils.hex8(bcdehlwz[regSet][0]) + ", " +
        "B' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][0]));
    System.out.println(
        "C = " + StringUtils.hex8(bcdehlwz[regSet][1]) + ", " +
        "C' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][1]));
    System.out.println(
        "D = " + StringUtils.hex8(bcdehlwz[regSet][2]) + ", " +
        "D' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][2]));
    System.out.println(
        "E = " + StringUtils.hex8(bcdehlwz[regSet][3]) + ", " +
        "E' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][3]));
    System.out.println(
        "H = " + StringUtils.hex8(bcdehlwz[regSet][4]) + ", " +
        "H' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][4]));
    System.out.println(
        "L = " + StringUtils.hex8(bcdehlwz[regSet][5]) + ", " +
        "L' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][5]));
    System.out.println(
        "W = " + StringUtils.hex8(bcdehlwz[regSet][6]) + ", " +
        "W' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][6]));
    System.out.println(
        "Z = " + StringUtils.hex8(bcdehlwz[regSet][7]) + ", " +
        "Z' = " + StringUtils.hex8(bcdehlwz[regSet ^ 1][7]));
    System.out.println("PC = " + StringUtils.hex16(pc));
    System.out.println("SP = " + StringUtils.hex16(sp));
    System.out.println("IX = " + StringUtils.hex16(index[0]));
    System.out.println("IY = " + StringUtils.hex16(index[1]));
  }
  
  public static void main(String[] args) {
    Regs regs = new Regs();
    
    regs.WR = true;
    regs.MODE_IN = MODE_8BIT;
    regs.REG_IN = REG_B;
    regs.DATA_IN = 19;
    regs.updateSeq();
    regs.printState();
 
    regs.WR = false;
    regs.EXX = true;
    regs.updateSeq();
    regs.printState();
  }
}
